#ifndef _Easy_Tcp_Server_
#define _Easy_Tcp_Server_
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>

#define  SOCKET int
#define INVALID_SOCKET  (SOCKET)(~0)
#define SOCKET_ERROR            (-1)
#endif

#include <iostream>
#include <stdio.h>
#include <vector>
#include "MessageHeader.hpp"

class EasyTcpServer
{
private:
	SOCKET _sock;
	std::vector<SOCKET> g_client;

public:
	EasyTcpServer()
	{
		_sock = INVALID_SOCKET;
		InitSocket();
	}
	virtual ~EasyTcpServer()
	{
		Close();
	}
	//初始化Socket
	SOCKET InitSocket()
	{
		//启动Win Sock 2.x环境
#ifdef _WIN32
		//启动Windows socket 2.x环境
		WORD ver = MAKEWORD(2, 2);
		WSADATA dat;
		WSAStartup(ver, &dat);
#endif

		//1.建立Socket
		if (INVALID_SOCKET != _sock)//检测是否建立了重复的SOCKET
		{
			printf("<socket = %d>关闭旧链接...", _sock);
			Close();
		}
		_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (INVALID_SOCKET == _sock)
		{
			printf("错误，Socket初始化失败...\n");
		}
		else
		{
			printf("<socket = %d>Socket初始化成功...\n", _sock);
		}
		return _sock;
	}

	//绑定IP和端口号
	int Bind(const char* ip, unsigned short port)
	{
		sockaddr_in _sin = {};
		_sin.sin_family = AF_INET;
		_sin.sin_port = htons(port);   //host to net unsigned short

#ifdef _WIN32
		if (ip)
		{
			_sin.sin_addr.S_un.S_addr = inet_addr(ip);
		}
		else
		{
			_sin.sin_addr.S_un.S_addr = INADDR_ANY; // inet_addr("127.0.0.1");
		}
#else
		if (ip)
		{
			_sin.sin_addr.s_addr = inet_addr(ip);
		}
		else
		{
			_sin.sin_addr.s_addr = INADDR_ANY; // inet_addr("127.0.0.1");
		}
#endif
		int ret = bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
		if (SOCKET_ERROR == ret)
		{
			printf("错误，绑定端口<port = %d>失败...\n", port);
		}
		else
		{
			printf("绑定端口<port = %d>成功...\n", port);
		}
		return ret;
	}
	//监听端口号 , n为待处理连接队列的最大长度。
	int Listen(int n)
	{
		int ret = listen(_sock, n);
		if (SOCKET_ERROR == ret)
		{
			printf("错误，<socket = %d>监听端口失败...\n", (int)_sock);
		}
		else
		{
			printf("<socket = %d>监听端口成功...\n", (int)_sock);
		}

		return ret;
	}
	//接受客户端连接
	SOCKET Accept()
	{
		sockaddr_in clientAddr = {};
		int nClineLen = sizeof(sockaddr_in);
		SOCKET cSocket = INVALID_SOCKET;

#ifdef _WIN32
		cSocket = accept(_sock, (sockaddr*)&clientAddr, &nClineLen);
#else
		cSocket = accept(_sock, (sockaddr*)&clientAddr, (socklen_t*)&nClineLen);
#endif
		if (INVALID_SOCKET == cSocket)
		{
			printf("<socket = %d>错误，接收到无效客户端SOCKET...\n", (int)_sock);
		}
		else
		{
			g_client.push_back(cSocket);
			NewUserJoin newUserJoin;
			SendData2All(&newUserJoin);

			printf("<socket = %d>新客户端<Socket = %d ,IP = %s >\n", (int)_sock, (int)cSocket, (inet_ntoa)(clientAddr.sin_addr));
		}
		return cSocket;
	}
	//关闭Socket
	void Close()
	{
		if (_sock != INVALID_SOCKET)
		{
#ifdef _WIN32
			for (int n = (int)g_client.size() - 1; n >= 0; n--)
			{
				closesocket(g_client[n]);
			}

			closesocket(_sock);

			//清除windows socket环境
			if (SOCKET_ERROR == WSACleanup())
			{
				printf("错误，终止使用Winsock 2 DLL失败...\n");
			}
			else
			{
				printf("终止使用Winsock 2 DLL成功...\n");
			}
#else
			for (size_t n = g_client.size() - 1; n >= 0; n--)
			{
				close(g_client[n]);
			}

			close(_sock);
#endif
		}
		_sock == INVALID_SOCKET;
	}
	//处理网络消息
	bool OnRun()
	{
		if (IsRun())
		{
			//伯克利s BSD socket
			fd_set fdRead, fdWrite, fdExp;
			//清理集合
			FD_ZERO(&fdRead);
			FD_ZERO(&fdWrite);
			FD_ZERO(&fdExp);

			//将描述符（SCOKET）加入集合
			FD_SET(_sock, &fdRead);
			FD_SET(_sock, &fdWrite);
			FD_SET(_sock, &fdExp);

			SOCKET maxSocket = _sock;
			for (int n = (int)g_client.size() - 1; n >= 0; n--) //不能用size_t来a-- ，他是无符号数
			{
				FD_SET(g_client[n], &fdRead);
				if (maxSocket < g_client[n])
					maxSocket = g_client[n];
			}

			//nfds是一个整数值 是指fd_set集合中所 描述符（socket)的范围，而不是数量
			//即是所有文件描述符最大值+1 在windows中该值可以为0
			timeval t = { 1,0 };
			int retSelect = select(maxSocket + 1, &fdRead, &fdWrite, &fdExp, &t);
			if (retSelect < 0)
			{
				printf("Select任务结束!\n");
				Close();
				return false;
			}
			//判断描述符是否在集合中
			bool test = FD_ISSET(_sock, &fdRead);
			if (test)
			{
				FD_CLR(_sock, &fdRead);
				//4.等待客户端连接
				Accept();
			}
#ifdef _WIN32
			for (size_t n = 0; n < fdRead.fd_count; n++)
			{
				if (-1 == RecvData(fdRead.fd_array[n]))
				{
					auto iter = find(g_client.begin(), g_client.end(), fdRead.fd_array[n]);
					if (iter != g_client.end())
					{
						g_client.erase(iter);
					}
				}
			}
#else
			//标准C++，同样适用于WINDOWS
			for (int n = (int)g_client.size() - 1; n >= 0; n--)
			{
				if (FD_ISSET(g_client[n], &fdRead))
				{
					if (-1 == RecvData(g_client[n]))
					{
						//auto ==> std::vector<SOCKET>::iterator
						auto iter = g_client.begin() + n;
						if (iter != g_client.end())
						{
							g_client.erase(iter);
						}
					}
				}
			}
#endif

			//printf("空闲时间处理其他业务...\n");
			//5.向客户端发送一条信息
			//send(cSocket, msgBuf, nMsgLen, 0);

			return true;
		}
		return false;

	}
	//是否工作中
	bool IsRun()
	{
		return _sock != INVALID_SOCKET;
	}
	//接收数据 处理粘包 拆分包
	int RecvData(SOCKET  cSocket)
	{
		//设置缓冲区
		char szResv[4096] = {};

		int nRecvLen = (int)recv(cSocket, (char*)&szResv, sizeof(Dataheader), 0);
		Dataheader* headerRv = (Dataheader*)szResv;

		if (nRecvLen <= 0)
		{
			printf("客户端<socket = %d>已退出，任务结束!\n", cSocket);
			return -1;
		}
		//if(nRecvLen >= headerRv->dataLength) 收到的数据要大于等于dataLength，在联机使用中需要进行判断

		recv(cSocket, szResv + sizeof(Dataheader), headerRv->dataLength - sizeof(Dataheader), 0);

		OnNetMsg(cSocket, headerRv);

		return 0;
	}
	//响应网络消息
	virtual void OnNetMsg(SOCKET  cSocket, Dataheader* header)
	{
		switch (header->cmd)
		{
		case CMD_LOGIN:
		{
			//忽略用户信息是否正确的判断
			Login* login = (Login*)header;
			printf("收到来自<socket = %d> 的CMD_LOGIN命令,长度：%d , 用户名:%s , 密码:%s .\n", cSocket, login->dataLength, login->userName, login->passWord);

			LoginResult retLogin;
			send(cSocket, (const char*)&retLogin, sizeof(LoginResult), 0);
			//SendData(cSocket, retLogin);
			break;
		}

		case CMD_LOGOUT:
		{
			//忽略用户信息是否正确的判断
			Logout* logout = (Logout*)header;
			printf("收到来自<socket = %d> 的CMD_LOGOUT命令,长度：%d , 用户名:%s .\n", cSocket, logout->dataLength, logout->userName);

			LogoutResult retLogout;
			send(cSocket, (const char*)&retLogout, sizeof(LogoutResult), 0);
			//SendData(cSocket, retLogout);
			break;
		}

		default:
		{
			Dataheader headerError = { CMD_ERROR,0 };
			send(cSocket, (const char*)&headerError, sizeof(Dataheader), 0);
			//SendData(cSocket, headerError);
			break;
		}

		}
	}
	//发送指定SOCKET数据
	int SendData(SOCKET  cSocket, Dataheader* headerSend)
	{
		if (IsRun() && headerSend)
		{
			return send(cSocket, (const char*)headerSend, headerSend->dataLength, 0);
		}
		return  SOCKET_ERROR;
	}

	//群发SOCKET数据
	void SendData2All(Dataheader* headerSend)
	{
		for (int n = (int)g_client.size() - 1; n >= 0; n--) //不能用size_t来做-- ，他是无符号数
		{
			SendData(g_client[n], headerSend);
		}
	}
};

#endif

